State 是 React Hook 非常重要的東西,多重要呢?簡單來講只要你需要更改呈現在畫面上的資料,那麼就一定脫離不了 React State。
在介紹 React 的 State 之前,我想先拉起 Vue 開發者的回憶,當我們需要將資料與畫面上的欄位做綁定時,就會使用 ref
or reactive
+ v-model
來達到資料的同步更新
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const text = ref('Hello Ray.');
return {
text,
}
}
});
app.mount('#app');
透過 v-model
指令就可以輕鬆達到修改資料,並同時觸發 re-render 的行為刷新畫面,而這就是所謂的雙向綁定機制,如果你對於這一塊雙向綁定機制有興趣的話,可以閱讀我先前寫的文章
請注意 Vue2 與 Vue3 的底層實作分為 Object.defineProperty
跟 ES6 Proxy
,因此是有差異的。
那麼 React 呢?React 也是類似的嗎?所以接著讓我們看 React 在針對資料上是如何處理的。
前面章節我們聊了許多 React 的東西,也稍微提到了 Vue 的雙向綁定機制,但是其實我們並沒有真正開始進入 React Hook 的介紹,一個 Hook 都沒有。
所以接著就要來介紹第一個 Hook,也就是 State Hook。
那麼 State 中文就是狀態的意思,是什麼的狀態呢?意指的資料狀態,因此其實與 Vue 的 ref
、reactive
非常相像,因為我們會需要傳入一個初始值給予 State 作為監聽使用。
但在 React 中稍微有一點不同,React Hook 的 State Hook 其實是在指 useState
這個函式,而基本的寫法如下:
const [myName, setMyName] = React.useState('Ray');
useState
函式會回傳一個陣列,通常我們會使用 陣列解構 取出兩個變數,第一個變數會作為渲染顯示用、單純的資料,第二個則是告知 React 資料有變化,所以要重新 re-render。
舉例來講當畫面有一顆按鈕,點擊後觸發替換內容,如果你不是使用 setMyName
的話是不會有任何效果的
const App = () => {
let [myName, setMyName] = React.useState('Ray');
return (
<div>
{ myName }
<button type="button" onClick={ () => myName = 'QQ' }>改名</button>
</div>
)
}
const app = document.querySelector('#app');
const root = ReactDOM.createRoot(app);
root.render(<App />);
主要原因是,如果你要修改變數的資料是必須使用第二個解構出來的變數才是正確姿勢,只是這邊要注意一件事情第二個解構出來的變數,會是一個函式
const App = () => {
let [myName, setMyName] = React.useState('Ray');
return (
<div>
{ myName }
<button type="button" onClick={ () => setMyName('QQ') }>改名</button>
</div>
)
}
const app = document.querySelector('#app');
const root = ReactDOM.createRoot(app);
root.render(<App />);
透過這個方式,我們可以發現在 React 裡面我們必須自己去 決定觸發函式的時機,這樣子畫面才會 re-render。
而第二個參數(setMyName
)就是在專門觸發 re-render 的,與我們在 Vue 時開發是不同的狀況,因為我們在撰寫 Vue 時,基本上是屬於即改即更新,而 React 則是可以自己決定何時更新畫面,這也是為什麼 React 會被說是資料屬於單向綁定,因為我們只是單純的把資料綁定畫面上,但資料就算更新了,我們也可以決定不更新,因此只要你不呼叫特定函式,那麼畫面也不會更新,反之 Vue 則是只要資料只要有變化,那麼畫面就必定會更新(除非你沒寫好或者踩雷了)。
在開始撰寫 Hook 之前,請切記前面章節所提的 React Hook 的規則:
因為這件事情很重要!
因為這件事情很重要!
因為這件事情很重要!
拉回到 useState
的部分,那麼 useState
做了什麼事情呢?我們傳入了一個字串之後,它會回傳一個陣列,裡面是 Array [ "Ray", dispatchSetState() ]
就如同前面所言,陣列解構的第一個會是一個單純的值,也就是你傳入的值,第二個則是一個函式,而 dispatchSetState
函式就是用來觸發 re-render 用的,因此當 myName
的值被更新時就會觸發畫面更新
const App = () => {
const [myName, setMyName] = React.useState('Ray');
return (
<div>
{ myName }
<button type="button" onClick={ () => setMyName('QQ 先生') }>更改名字</button>
</div>
)
}
const app = document.querySelector('#app');
const root = ReactDOM.createRoot(app);
root.render(<App />);
當你按下「更改名字」的按鈕時,就會發現畫面上輸出的字串也會跟著被重新渲染。
但如果今天沒有使用 useState
呢?那有辦法更新嗎?其實實驗是最好的學習方式,因此你可以試著不使用 useState
然後直接宣告一個變數命修改值
const App = () => {
let myName = 'Ray';
return (
<div>
{ myName }
<button type="button" onClick={ () => myName = 'QQ 先生' }>更改名字</button>
</div>
)
}
const app = document.querySelector('#app');
const root = ReactDOM.createRoot(app);
root.render(<App />);
透過上方各種範例,我們就可以知道 useState
是一個觸發畫面重新渲染的關鍵 Hook,也是必須一定要掌握的 Hook。
而 useState
一樣是可以傳入各式各樣的型別的
const App = () => {
const [str, setStr] = React.useState('Ray');
const [num, setNum] = React.useState(0);
const [bool, setBool] = React.useState(true);
return (
<div>
<p>String Value:{ str }</p>
<button type="button" onClick={ () => setStr('QQ') }>更改</button>
<hr/>
<p>Number Value:{ num }</p>
<button type="button" onClick={ () => setNum(num + 1) }>更改</button>
<hr/>
<p>Boolean Value:{ bool.toString() }</p>
<button type="button" onClick={ () => setBool(!bool) }>更改</button>
<hr/>
</div>
)
}
const app = document.querySelector('#app');
const root = ReactDOM.createRoot(app);
root.render(<App />);
相信冰雪聰明的你已經發現了陣列跟物件沒有在上面中,主要原因是這兩個東西必須用另一種方式撰寫
<div>
<p>Array Value:{ arr }</p>
<button type="button" onClick={ () => setArr([...arr, 1]) }>更改</button>
<p>Array Value:{ arr }</p>
<button type="button" onClick={ () => setArr(oldArray => [...oldArray, 1]) }>更改</button>
<p>Object Value:{ JSON.stringify(obj, null, 2) }</p>
<button type="button" onClick={ () => setObj({ myName: 'QQ' }) }>更改</button>
<hr/>
</div>
透過前面許多的範例跟練習,我們可以知道為什麼人家常常會說 React 對於 JavaScript 基礎功吃得很重,畢竟光是 useState
你就必須理解陣列解構,更不用說物件解構等寫法。
本文將會同步更新到我的部落格